<?php


namespace Commands;

use App\Model\Server\Auth;
use Database\DatabasesProviders;
use Database\Db;
use ReflectionException;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;


/**
 * Class BackViewController
 * @package commands
 */
class BackViewController extends Command
{


    /**
     */
    public function __construct()
    {
        parent::__construct('back:view:create');
        $this->addOption('table', null, InputArgument::OPTIONAL, '', 'server');
        $this->addOption('server', null, InputArgument::OPTIONAL, '', 'im_relation');
        $this->addOption('prefix', null, InputArgument::OPTIONAL, '', 'im_');
        $this->addOption('split', null, InputArgument::OPTIONAL, '', '_');
        $this->addOption('module', null, InputArgument::OPTIONAL, '', 'heart-bottle');
        $this->addOption('isEdit', null, InputOption::VALUE_NONE, '');
        $this->addOption('webroot', null, InputArgument::OPTIONAL, '', '/mnt/e/server-admin');
        $this->setDescription('php kiri.php back:view:create --table u_user --server users --prefix u_ --split _ --module system');
    }


    /**
     * @param InputInterface $input
     * @param OutputInterface $output
     * @return int
     * @throws ReflectionException
     */
    public function execute(InputInterface $input, OutputInterface $output): int
    {
        $tabel = $input->getOption('table');
        if (empty($tabel)) {
            $output->write('数据表不能为空');
            return 0;
        }

        $server  = $input->getOption('server');
        $prefix  = $input->getOption('prefix');
        $split   = $input->getOption('split');
        $module  = $input->getOption('module');
        $webroot = $input->getOption('webroot');

        $connection = \Kiri::getDi()->get(DatabasesProviders::class)->get($server);
        $desc       = Db::desc($tabel, $connection);
        if (empty($desc)) {
            $output->write('系统繁忙, 请稍后再试!');
            return 0;
        }

        $modelName = str_replace($prefix, '', $tabel);
        if (!empty($split)) {
            $modelName = explode($split, $modelName);
        } else {
            $modelName = [$modelName];
        }

        $modelName = array_filter($modelName);

        $model      = $this->getModelName($modelName);
        $moduleName = $this->getModelName(explode('-', $module));

        $dis = $input->getOption('webroot') . '/src/app/core';

        if (!is_writable($dis)) {
            $output->write('目录不可写');
            return 0;
        }

        $dis = rtrim($dis, '/') . '/';

        if (!empty($module)) {
            $dis .= $module . '/';
            if (!is_dir($dis)) {
                mkdir($dis);
            }
        }

        $prefix = str_replace('_', '-', $connection->database);

        $classPrefix = $this->getModelName(explode('-', $prefix));

        $newName = $prefix . '-' . implode('-', $modelName);

        $contexts           = [];
        $actionPath         = file_get_contents(__DIR__ . '/tmp/actions.template');
        $contexts['action'] = str_replace('「$model」', $classPrefix . $model, $actionPath);
        $contexts['action'] = str_replace('「$lower」', $newName, $contexts['action']);
        $path               = $dis . '_actions/';
        if (!is_dir($path)) {
            mkdir($path);
        }
        file_put_contents($path . $prefix . '-' . implode('-', $modelName) . '.actions.ts', $contexts['action']);

        $actionPath             = file_get_contents(__DIR__ . '/tmp/datasource.template');
        $contexts['datasource'] = str_replace('「$model」', $classPrefix . $model, $actionPath);
        $contexts['datasource'] = str_replace('「$lower」', $newName, $contexts['datasource']);
        $path                   = $dis . '_data-sources/';
        if (!is_dir($path)) {
            mkdir($path);
        }
        file_put_contents($path . $prefix . '-' . implode('-', $modelName) . '.datasource.ts', $contexts['datasource']);

        $actionPath          = file_get_contents(__DIR__ . '/tmp/effects.template');
        $contexts['effects'] = str_replace('{$model}', $classPrefix . $model, $actionPath);
        $contexts['effects'] = str_replace('「$fmodel」', lcfirst($classPrefix . $model), $contexts['effects']);
        $contexts['effects'] = str_replace('「$lower」', $newName, $contexts['effects']);
        $path                = $dis . '_effects/';
        if (!is_dir($path)) {
            mkdir($path);
        }
        file_put_contents($path . $prefix . '-' . implode('-', $modelName) . '.effects.ts', $contexts['effects']);

        $actionPath        = file_get_contents(__DIR__ . '/tmp/model.template');
        $contexts['model'] = str_replace('「$model」', $classPrefix . $model, $actionPath);
        $contexts['model'] = str_replace('「$lower」', $newName, $contexts['model']);


        [$fields, $clears, $controls, $views] = $this->fomart($desc);

        $contexts['model'] = str_replace('$fields', implode('', $fields), $contexts['model']);
        $contexts['model'] = str_replace('$clear', implode('', $clears), $contexts['model']);

        $path = $dis . '_models/';
        if (!is_dir($path)) {
            mkdir($path);
        }
        file_put_contents($path . $prefix . '-' . implode('-', $modelName) . '.model.ts', $contexts['model']);


        $actionPath           = file_get_contents(__DIR__ . '/tmp/reducers.template');
        $contexts['reducers'] = str_replace('「$model」', $classPrefix . $model, $actionPath);
        $contexts['reducers'] = str_replace('「$lower」', $newName, $contexts['reducers']);
        $path                 = $dis . '_reducers/';
        if (!is_dir($path)) {
            mkdir($path);
        }
        file_put_contents($path . $prefix . '-' . implode('-', $modelName) . '.reducers.ts', $contexts['reducers']);


        $actionPath            = file_get_contents(__DIR__ . '/tmp/selectoers.template');
        $contexts['selectors'] = str_replace('「$model」', $classPrefix . $model, $actionPath);
        $contexts['selectors'] = str_replace('「$lower」', $newName, $contexts['selectors']);
        $path                  = $dis . '_selectors/';
        if (!is_dir($path)) {
            mkdir($path);
        }
        file_put_contents($path . $prefix . '-' . implode('-', $modelName) . '.selectors.ts', $contexts['selectors']);


        $actionPath           = file_get_contents(__DIR__ . '/tmp/service.template');
        $contexts['services'] = str_replace('「$model」', $classPrefix . $model, $actionPath);
        $contexts['services'] = str_replace('「$module」', $moduleName, $contexts['services']);
        $contexts['services'] = str_replace('「$lower」', $newName, $contexts['services']);
        $path                 = $dis . '_services/';
        if (!is_dir($path)) {
            mkdir($path);
        }
        file_put_contents($path . $prefix . '-' . implode('-', $modelName) . '.service.ts', $contexts['services']);

        $modelName = implode('-', $modelName);


        $content = file_get_contents($dis . '/index.ts');

        $actions = PHP_EOL . PHP_EOL . '
export {
	' . $classPrefix . $model . 'ActionTypes,
	' . $classPrefix . $model . 'Actions,
	' . $classPrefix . $model . 'OnServerCreated,
	' . $classPrefix . $model . 'Created,
	' . $classPrefix . $model . 'Updated,
	' . $classPrefix . $model . 'StatusUpdated,
	One' . $classPrefix . $model . 'Deleted,
	Many' . $classPrefix . $model . 'Deleted,
	' . $classPrefix . $model . 'PageRequested,
	' . $classPrefix . $model . 'PageLoaded,
	' . $classPrefix . $model . 'PageCancelled,
	' . $classPrefix . $model . 'PageToggleLoading
} from \'./_actions/' . $prefix . '-' . $modelName . '.actions\';

export {
	select' . $classPrefix . $model . 'ById,
	select' . $classPrefix . $model . 'PageLoading,
	selectLastCreated' . $classPrefix . $model . 'Id,
	select' . $classPrefix . $model . 'InStore,
	// selectHas' . $classPrefix . $model . 'sInStore,
	// select' . $classPrefix . $model . 'sPageLastQuery,
	select' . $classPrefix . $model . 'ActionLoading,
	select' . $classPrefix . $model . 'ShowInitWaitingMessage
} from \'./_selectors/' . $prefix . '-' . $modelName . '.selectors\';
export {' . $classPrefix . $model . 'Service} from \'./_services/' . $prefix . '-' . $modelName . '.service\';
';
        $old     = preg_replace('/[\n\r\t\s]+/', '', $content);
        $new     = preg_replace('/[\n\r\t\s]+/', '', $actions);

        if (!str_contains($old, $new)) {
            file_put_contents($dis . '/index.ts', $actions, FILE_APPEND);
        }

        if (!empty($webroot) && is_dir($webroot)) {
            $path = explode('/', $input->getOption('webroot'));

            $modelName = preg_replace('/(\d+)/', 'm$1', $modelName);

            shell_exec('cd ' . implode(DIRECTORY_SEPARATOR, $path) . ' && npm run ng generate component views/pages/' . $module . '/' . $prefix . '-' . $modelName);
            $isEdit = (int)$input->getOption('isEdit');
            if ($isEdit == 1) {
                shell_exec('cd ' . implode(DIRECTORY_SEPARATOR, $path) . ' && npm run ng generate component views/pages/' . $module . '/' . $prefix . '-' . $modelName . '/' . $prefix . '-' . $modelName . '-edit');
            }

            $actionPath = file_get_contents(__DIR__ . '/tmp/component.template');
            $actionPath = str_replace('{$modelName}', $classPrefix . $model, $actionPath);
            $actionPath = str_replace('{$model}', $prefix . '-' . $modelName, $actionPath);
            $actionPath = str_replace('{$module}', $module, $actionPath);

            [$fieldHtml, $_fields] = $this->grid($desc);

            $path[] = 'src';
            $path[] = 'app';

            $path       = implode('/', $path) . '/views/pages/' . $module . '/' . $prefix . '-' . $modelName;
            $actionPath = str_replace('{$fields}', '\'' . implode('\',\'', $_fields) . '\'', $actionPath);
            file_put_contents($path . '/' . $prefix . '-' . $modelName . '.component.ts', $actionPath);

            $actionPath = file_get_contents(__DIR__ . '/tmp/view.template');
            $actionPath = str_replace('{$fieldHtml}', $fieldHtml, $actionPath);
            $actionPath = str_replace('{$auth}', $prefix . '/' . $modelName, $actionPath);
            $actionPath = str_replace('{$module}', $module, $actionPath);
            file_put_contents($path . '/' . $prefix . '-' . $modelName . '.component.html', $actionPath);


            $this->setAuths($module, $prefix . '/' . $modelName);

            if ($isEdit == 1) {
                $path       = $path . '/' . $prefix . '-' . $modelName . '-edit';
                $actionPath = file_get_contents(__DIR__ . '/tmp/edit-component.template');
                $actionPath = str_replace('{$modelName}', $classPrefix . $model, $actionPath);
                $actionPath = str_replace('{$model}', $prefix . '-' . $modelName, $actionPath);
                $actionPath = str_replace('{$module}', $module, $actionPath);

                $actionPath = str_replace('{$controls}', implode($controls), $actionPath);
                file_put_contents($path . '/' . $prefix . '-' . $modelName . '-edit.component.ts', $actionPath);

                $actionPath = file_get_contents(__DIR__ . '/tmp/edit-view.template');
                $actionPath = str_replace('{$htmls}', implode('
           	<div class="kt-separator kt-separator--dashed"></div>
           	
           	', $views), $actionPath);
                $actionPath = str_replace('{$modelName}', $classPrefix . $model, $actionPath);
                file_put_contents($path . '/' . $prefix . '-' . $modelName . '-edit.component.html', $actionPath);
            }

            shell_exec('cd ' . $webroot . ' && git add . && git commit . -m \'add view ' . $classPrefix . $model . '\' && git push');
        }

        $output->write("Please add routes to api \n" .
                       $classPrefix . $model . ': {
	Add: HOST + "' . $prefix . '/' . $modelName . '/add",
	List: HOST + "' . $prefix . '/' . $modelName . '/list",
	Update: HOST + "' . $prefix . '/' . $modelName . '/update",
	Auditing: HOST + "' . $prefix . '/' . $modelName . '/auditing",
	BatchAuditing: HOST + "' . $prefix . '/' . $modelName . '/batch-auditing",
	BatchDelete: HOST + "' . $prefix . '/' . $modelName . '/batch-delete",
	Delete: HOST + "' . $prefix . '/' . $modelName . '/delete",
	Detail: HOST + "' . $prefix . '/' . $modelName . '/detail",
}'
        );
        return 1;
    }


    /**
     * @param $prefix
     * @param $modelName
     * @return void
     * @throws \Exception
     */
    private function setAuths($prefix, $modelName): void
    {
        $auth = [
            [
                'module'     => $prefix,
                'alias'      => $prefix . '_' . $modelName . '_add',
                'path'       => $prefix . '/' . $modelName . '/add',
                'addTime'    => time(),
                'modifyTime' => time(),
            ], [
                'module'     => $prefix,
                'alias'      => $prefix . '_' . $modelName . '_detail',
                'path'       => $prefix . '/' . $modelName . '/detail',
                'addTime'    => time(),
                'modifyTime' => time(),
            ], [
                'module'     => $prefix,
                'alias'      => $prefix . '_' . $modelName . '_update',
                'path'       => $prefix . '/' . $modelName . '/update',
                'addTime'    => time(),
                'modifyTime' => time(),
            ], [
                'module'     => $prefix,
                'alias'      => $prefix . '_' . $modelName . '_auditing',
                'path'       => $prefix . '/' . $modelName . '/auditing',
                'addTime'    => time(),
                'modifyTime' => time(),
            ], [
                'module'     => $prefix,
                'alias'      => $prefix . '_' . $modelName . '_batch-auditing',
                'path'       => $prefix . '/' . $modelName . '/batch-auditing',
                'addTime'    => time(),
                'modifyTime' => time(),
            ], [
                'module'     => $prefix,
                'alias'      => $prefix . '_' . $modelName . '_batch-delete',
                'path'       => $prefix . '/' . $modelName . '/batch-delete',
                'addTime'    => time(),
                'modifyTime' => time(),
            ], [
                'module'     => $prefix,
                'alias'      => $prefix . '_' . $modelName . '_delete',
                'path'       => $prefix . '/' . $modelName . '/delete',
                'addTime'    => time(),
                'modifyTime' => time(),
            ], [
                'module'     => $prefix,
                'alias'      => $prefix . '_' . $modelName . '_list',
                'path'       => $prefix . '/' . $modelName . '/list',
                'addTime'    => time(),
                'modifyTime' => time(),
            ]
        ];

        foreach ($auth as $item) {
            $auth = Auth::query()->where(['module' => $item['module'], 'path' => $item['path']])->first();
            if (empty($auth)) {
                Auth::inserts([$item]);
            }
        }

    }


    public $type = ['tinyint', 'smallint', 'mediumint', 'int', 'bigint', 'timestamp', 'float', 'double', 'decimal'];

    private function fomart($fields)
    {
        $_tmp = $_clear = $controls = $views = [];
        foreach ($fields as $key => $val) {
            $type    = strtolower(preg_replace('/\(\d+\)/', '', $val['Type']));
            $views[] = '<div class="form-group kt-form__group row">
                <label class="col-2 col-form-label">' . $val['Field'] . ':</label>
                <div class="col-lg-9 kt-margin-bottom-20-mobile">
                    <mat-form-field class="mat-form-field-fluid">
                        <input matInput placeholder="Enter ' . $val['Field'] . '" formControlName="' . $val['Field'] . '"/>
                        <mat-error>' . $val['Field'] . ' is
                            <strong>required</strong>
                        </mat-error>
                        <mat-hint align="start">Please enter
                            <strong>' . $val['Field'] . '</strong>
                        </mat-hint>
                    </mat-form-field>
                </div>
            </div>';

            if (empty($controls)) {
                $controls[] = $val['Field'] . ': [this.customer.' . $val['Field'] . ', Validators.required],';
            } else {
                $controls[] = '
			' . $val['Field'] . ': [this.customer.' . $val['Field'] . ', Validators.required],';
            }
            if (in_array($type, $this->type)) {
                $_tmp[]   = '
	' . $val['Field'] . ': number;';
                $_clear[] = '
		this.' . $val['Field'] . ' = 0;';
            } else if ($type == 'json') {
                $_tmp[]   = '
	' . $val['Field'] . ': any[];';
                $_clear[] = '
		this.' . $val['Field'] . ' = [];';
            } else {
                $_tmp[]   = '
	' . $val['Field'] . ': string;';
                $_clear[] = '
		this.' . $val['Field'] . ' = \'\';';
            }
        }

        return [$_tmp, $_clear, $controls, $views];
    }

    private function grid($fields)
    {
        $names = $_fields = [];
        foreach ($fields as $field) {
            $_fields[] = $field['Field'];
            $names[]   = '
                <ng-container matColumnDef="' . $field['Field'] . '">
					<!-- ATTRIBUTE mat-sort-header  for sorting | https://material.angular.io/components/sort/overview -->
					<mat-header-cell *matHeaderCellDef mat-sort-header>' . $field['Field'] . '</mat-header-cell>
					<mat-cell *matCellDef="let model">{{model.' . $field['Field'] . '}}</mat-cell>
				</ng-container>';
        }
        return [implode(PHP_EOL, $names), $_fields];
    }


    private function getModelName(array $models)
    {
        foreach ($models as $key => $val) {
            $models[$key] = ucfirst($val);
        }
        return implode('', $models);
    }

}
